package com.inspur.edp.udt.designtime.api.json;


import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.inspur.edp.cef.designtime.api.json.CefNames;
import com.inspur.edp.cef.designtime.api.json.SerializerUtils;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UseTypeInfo;
import com.inspur.edp.udt.designtime.api.entity.enumtype.UseType;
import com.inspur.edp.udt.designtime.api.entity.validation.UdtTriggerTimePointType;
import com.inspur.edp.udt.designtime.api.entity.validation.ValidationInfo;
import com.inspur.edp.udt.designtime.api.exception.UdtModelErrorCodeEnum;
import com.inspur.edp.udt.designtime.api.exception.UdtModelException;
import com.inspur.edp.udt.designtime.api.extension.BaseUdtExtension;
import com.inspur.edp.udt.designtime.api.extension.BaseUdtExtensionDeserializer;
import com.inspur.edp.udt.designtime.api.extension.UdtExtensionConfig;
import com.inspur.edp.udt.designtime.api.extension.UdtExtensionConfigs;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;

/**
 * UDT序列化基类
 */
public abstract class UdtDeserializer extends JsonDeserializer<UnifiedDataTypeDef> {
	/**
	 *
	 */

	protected abstract void beforeUdtDeserializer(UnifiedDataTypeDef dataType);

	@Override
	public UnifiedDataTypeDef deserialize(JsonParser jsonParser, DeserializationContext object) {
		UnifiedDataTypeDef dataType = createUnifiedDataType();
		SerializerUtils.readStartObject(jsonParser);
		dataType.setDescription("");
        dataType.setCreator("");
        dataType.setBeLabel(new ArrayList<>());
        dataType.setAssemblyName("");
        dataType.setModifier("");
	    dataType.getPropertyUseTypeInfos();
	    dataType.getValidations();
		beforeUdtDeserializer(dataType);

		while (jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) {
			readBasicInfo(jsonParser, dataType,object);
		}
		SerializerUtils.readEndObject(jsonParser);
		if(dataType.getI18nResourceInfoPrefix()==null||"".equals(dataType.getI18nResourceInfoPrefix())){
			dataType.setI18nResourceInfoPrefix(dataType.getDotnetAssemblyName()+"."+dataType.getCode());
		}
		return dataType;
	}

	/**
	 * 反序列化
	 */
	private void readBasicInfo(JsonParser jsonParser, UnifiedDataTypeDef dataType,DeserializationContext object) {
		String propertyName = SerializerUtils.readPropertyName(jsonParser);
		switch (propertyName) {
			case UdtNames.Id:
				dataType.setId(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Code:
				dataType.setCode(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Name:
				dataType.setName(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.BeLabel:
				dataType.setBeLabel(SerializerUtils.readStringArray(jsonParser));
				break;
			case UdtNames.Description:
				dataType.setDescription(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Creator:
				dataType.setCreator(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.CreatedDate:
				dataType.setCreatedDate(readDateTime(jsonParser));
				break;
			case UdtNames.Modifier:
				dataType.setModifier(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.ModifiedDate:
				dataType.setModifiedDate(readDateTime(jsonParser));
				break;
			case UdtNames.AssemblyName:
				String strTemp = SerializerUtils.readPropertyValue_String(jsonParser);
				dataType.setAssemblyName(handleGeneratingAssembly(strTemp));
				dataType.setDotnetAssemblyName(strTemp);//获取N版下config
				break;
			case UdtNames.PropertyUseTypeInfos:
				readPropertyUseTypeInfos(jsonParser, dataType);
				break;
			case UdtNames.ValidationInfos:
				readValidationInfos(jsonParser, dataType);
				break;
			case CefNames.I18nResourceInfoPrefix:
				dataType.setI18nResourceInfoPrefix(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Extensions:
				readExtensions(dataType,jsonParser,object);
				break;
			default:
				readBasicInfo(jsonParser, dataType, propertyName);
				break;
		}
	}

	protected void readExtensions(UnifiedDataTypeDef dataType, JsonParser jsonParser,DeserializationContext object)
	{
		SerializerUtils.readStartObject(jsonParser);
		while (jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) {
			String extendType = SerializerUtils.readPropertyName(jsonParser);
			UdtExtensionConfig config =UdtExtensionConfigs.getInstance().getExtensionConfig(extendType);
			try {
				BaseUdtExtensionDeserializer deserializer= (BaseUdtExtensionDeserializer) Class.forName(config.getDeserclass()).newInstance();
				dataType.getUdtExtensions().put(extendType, (BaseUdtExtension) deserializer.deserialize(jsonParser,object));
			} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | IOException e) {
				throw UdtModelException.createException(UdtModelErrorCodeEnum.GSP_BEMODEL_COMMON_0002, config.getDeserclass());
			}

        }

		SerializerUtils.readEndObject(jsonParser);
	}

	private String handleGeneratingAssembly(String readPropertyValue_string) {
		String[] list = readPropertyValue_string.split("\\.");
		String result = "";

		for (int i = 0; i < list.length; i++) {
			String lowerCase = list[i].toLowerCase();
			if (i == 0) {
				if (lowerCase.equals("inspur")) {
					result = result.concat("com.inspur");
					continue;
				} else {
					result = result.concat(lowerCase);
					continue;
				}
			}
			result = result.concat(".");
			result = result.concat(lowerCase);
		}
		if (result.startsWith("com.inspur.gsp.common.commonudt")) {
			result = result.replace("com.inspur.gsp.common.commonudt", "com.inspur.edp.common.commonudt");
		}
		return result;
	}

	DateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
	DateDeserializers.DateDeserializer dateDeserializer = new DateDeserializers.DateDeserializer(
			new DateDeserializers.DateDeserializer(), format, format.toString());

	private Date readDateTime(JsonParser jsonParser) {
		try {
			Date result = UdtNames.DateTimeFormat.parse(jsonParser.getValueAsString());
//			jsonParser.nextValue();
			jsonParser.nextToken();
			return result;
		} catch (IOException | ParseException e) {
			throw UdtModelException.createException(UdtModelErrorCodeEnum.GSP_BEMODEL_JSON_0001, e, "DateTime");
		}
//		try {
//			Date result = dateDeserializer.deserialize(jsonParser, null);
////while(true){
////	JsonToken token = jsonParser.getCurrentToken();
////	System.out.println(token.toString());
////	jsonParser.nextValue();
////}
//			jsonParser.nextToken();
//			return result;
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		return null;
	}

	private void readPropertyUseTypeInfos(JsonParser jsonParser, UnifiedDataTypeDef dataType) {
		SerializerUtils.readStartArray(jsonParser);

		while (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
			SerializerUtils.readStartObject(jsonParser);
			UseTypeInfo info = readUseTypeInfo(jsonParser);
			if (dataType.getPropertyUseTypeInfos().containsKey(info.getPropertyName())) {
				dataType.getPropertyUseTypeInfos().remove(info.getPropertyName());
			}
			dataType.getPropertyUseTypeInfos().put(info.getPropertyName(), info);
			SerializerUtils.readEndObject(jsonParser);
		}
		SerializerUtils.readEndArray(jsonParser);
	}

	private UseTypeInfo readUseTypeInfo(JsonParser jsonParser) {
		UseTypeInfo info = new UseTypeInfo();
		while (jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) {
			readUseTypeInfo(jsonParser, info);
		}

		return info;
	}

	private void readUseTypeInfo(JsonParser jsonParser, UseTypeInfo info) {
		String propertyName = SerializerUtils.readPropertyName(jsonParser);
		switch (propertyName) {
			case UdtNames.PropertyUseType:
				info.setPropertyUseType(SerializerUtils.readPropertyValue_Enum(jsonParser, UseType.class, UseType.values()));
				break;
			case UdtNames.PropertyName:
				info.setPropertyName(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.CanEdit:
				info.setCanEdit((SerializerUtils.readPropertyValue_boolean(jsonParser)));
				break;
		}

	}

	private void readValidationInfos(JsonParser jsonParser, UnifiedDataTypeDef dataType) {
		SerializerUtils.readStartArray(jsonParser);

		while (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
			SerializerUtils.readStartObject(jsonParser);
			ValidationInfo info = readValidation(jsonParser);
			dataType.getValidations().add(info);
			SerializerUtils.readEndObject(jsonParser);
		}

		SerializerUtils.readEndArray(jsonParser);
	}

	// 兼容没有触发时机的情况，若之前并未设置触发时机，则默认为保存前时机
	private boolean hasReadTriggerTimeType = false;

	private ValidationInfo readValidation(JsonParser jsonParser) {
		ValidationInfo info = new ValidationInfo();
		hasReadTriggerTimeType = false;
		while (jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) {
			readValidation(jsonParser, info);
		}
		if (hasReadTriggerTimeType == false) {
			info.setTriggerTimePointType(EnumSet.of(UdtTriggerTimePointType.BeforeSave));
		}
		return info;
	}

	private void readValidation(JsonParser jsonParser, ValidationInfo info) {
		String propertyName = SerializerUtils.readPropertyName(jsonParser);
		switch (propertyName) {
			case UdtNames.Id:
				info.setId(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Code:
				info.setCode(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Name:
				info.setName(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Description:
				info.setDescription(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.CmpId:
				info.setCmpId(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.CmpPkgName:
				info.setCmpPkgName(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.CmpName:
				info.setCmpName(SerializerUtils.readPropertyValue_String(jsonParser));
				break;
			case UdtNames.Order:
				info.setOrder(SerializerUtils.readPropertyValue_Integer(jsonParser));
				break;
			case UdtNames.TriggerTimePointType:
				info.setTriggerTimePointType(readGetUdtTriggerTimePointType(jsonParser));
				hasReadTriggerTimeType = true;
				break;
			case UdtNames.IsGenerateComponent:
				info.setIsGenerateComponent(SerializerUtils.readPropertyValue_boolean(jsonParser));
				break;
			case UdtNames.RequestElements:
				readRequestElements(jsonParser, info);
				break;


		}

	}

	private EnumSet<UdtTriggerTimePointType> readGetUdtTriggerTimePointType(JsonParser jsonParser) {
		EnumSet<UdtTriggerTimePointType> result = EnumSet.noneOf(UdtTriggerTimePointType.class);
		int intValueSum = SerializerUtils.readPropertyValue_Integer(jsonParser);
		UdtTriggerTimePointType[] values = UdtTriggerTimePointType.values();
		for (int i = values.length - 1; i >= 0; i--) {
			UdtTriggerTimePointType value = values[i];
			if (intValueSum > 0 && intValueSum >= value.getValue()) {
				result.add(value);
				intValueSum -= value.getValue();
			}
		}
		return result;
	}

	private void readRequestElements(JsonParser jsonParser, ValidationInfo info) {
		if (SerializerUtils.readNullObject(jsonParser)) {
			return;
		}

		SerializerUtils.readStartArray(jsonParser);

		while (jsonParser.getCurrentToken() == JsonToken.VALUE_STRING) {
			String eleId = SerializerUtils.readPropertyValue_String(jsonParser);
			info.getRequestElements().add(eleId);
		}
		SerializerUtils.readEndArray(jsonParser);
	}

	protected abstract UnifiedDataTypeDef createUnifiedDataType();

	protected abstract void readBasicInfo(JsonParser reader, UnifiedDataTypeDef dataType, String propertyName);

}